package com.ymttest.utils;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.junit.experimental.categories.Category;
import org.junit.runner.JUnitCore;
import org.junit.runner.Request;
import org.junit.runner.Result;

import com.ymatou.ResultParser;
import com.ymatou.database.Wapper.TestCaseWapper;
import com.ymatou.iapi.email.parameter.SendEmailBean;
import com.ymatou.iapi.email.service.SendEmailCall;
import com.ymatou.iapi.godmch.parameter.listagentTypeBean;
import com.ymt.core.tool.Logger;

/************************************************************************************
 * @File name :RunTests.java
 * @Author : zhouyi
 * @Date : 2015年9月1日
 * @Copyright : 洋码头
 ************************************************************************************/
class TestSuiteBeanV2 {

	// 接口Ts_XX 类
	private Class<?> testSuiteClass;
	// 接口内 Tc_xx 方法名称
	private String testCaseName;

	public Class<?> getTestSuiteClass() {
		return testSuiteClass;
	}

	public void setTestSuiteClass(Class<?> cls) {
		this.testSuiteClass = cls;
	}

	public String getTestCaseName() {
		return testCaseName;
	}

	public void setTestCaseName(String testCaseName) {
		this.testCaseName = testCaseName;
	}

	/* （非 Javadoc）
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return "TestSuiteBeanV2 [testSuiteClass=" + testSuiteClass + ", testCaseName=" + testCaseName + "]";
	}
	
	

}

public class RunTestsV2 {

	private static int threadNum = 1;

	private static ExecutorService SERVICE;

	private static List<Result> RESULTS = new ArrayList<Result>();
	// 结果文件路径
	private static String RESULT_PATH = new File("").getAbsolutePath() + File.separator + "Results";

	/**
	 * @param runsuite
	 *            需要执行的case类列表
	 */
	public static List<Result> Run(List<TestSuiteBeanV2> testCaseList, boolean isshuffle) {
		// 删除结果xml 文件
		deleteResultFile(new File(RESULT_PATH));
		// 打乱用例的执行的顺序，尽量避免同一个接口下多个用例同时并发执行
		if (isshuffle) {
			Collections.shuffle(testCaseList);
		}

		if (SERVICE == null || SERVICE.isShutdown()) {
			SERVICE = Executors.newFixedThreadPool(threadNum);
		}
		CountDownLatch countDownLatch = new CountDownLatch(testCaseList.size());
		for (TestSuiteBeanV2 TestSuiteBeanV2 : testCaseList) {

			Class junitClass = TestSuiteBeanV2.getTestSuiteClass();
			String methodName = TestSuiteBeanV2.getTestCaseName();

			SERVICE.execute(() -> {

				Result r = RunCase(junitClass, methodName);
				// @Ignore 的case 执行返回结果为null，排除掉
				if (r != null) {
					RESULTS.add(r);
				}
				countDownLatch.countDown();
			});
		}
		try {
			// 等待线程池里所有线程任务结束
			countDownLatch.await();
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info("运行error:" + e.getStackTrace());
		} finally {
			SERVICE.shutdown();
		}
		return RESULTS;
	}
	/**
	 * @param testCaseList
	 * @param isshuffle
	 * @param resetfile 为true的时候会删除原来的文件
	 * @return
	 */
	public static List<Result> Run(List<TestSuiteBeanV2> testCaseList, boolean isshuffle,boolean resetfile) {
		// 删除结果xml 文件
		if(resetfile){
			deleteResultFile(new File(RESULT_PATH));
		}
		// 打乱用例的执行的顺序，尽量避免同一个接口下多个用例同时并发执行
		if (isshuffle) {
			Collections.shuffle(testCaseList);
		}

		if (SERVICE == null || SERVICE.isShutdown()) {
			SERVICE = Executors.newFixedThreadPool(threadNum);
		}
		CountDownLatch countDownLatch = new CountDownLatch(testCaseList.size());
		for (TestSuiteBeanV2 TestSuiteBeanV2 : testCaseList) {

			Class junitClass = TestSuiteBeanV2.getTestSuiteClass();
			String methodName = TestSuiteBeanV2.getTestCaseName();

			SERVICE.execute(() -> {

				Result r = RunCase(junitClass, methodName);
				// @Ignore 的case 执行返回结果为null，排除掉
				if (r != null) {
					RESULTS.add(r);
				}
				countDownLatch.countDown();
			});
		}
		try {
			// 等待线程池里所有线程任务结束
			countDownLatch.await();
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info("运行error:" + e.getStackTrace());
		} finally {
			SERVICE.shutdown();
		}
		return RESULTS;
	}
	/**
	 * 单个用例执行方法
	 * 
	 * @param junitClass
	 *            执行的junit 类带包名 eg.com.ymatou.query.trading.api.testcase.
	 *            Ts_ExistsSellerOrderList
	 * @param testCaseMethod
	 *            junit 类里的用例方法名 eg.Tc_ExistsSellerOrderList_001
	 */
	public static Result RunCase(Class junitClass, String testCaseMethod) {
		Result result = new Result();
		JUnitCore junitRunner = new JUnitCore();
		Request request = Request.method(junitClass, testCaseMethod);
		result = junitRunner.run(request);
		Logger.info("Run " + testCaseMethod + " Duration : " + result.getRunTime() + "ms");
		return result;
	}

	/**
	 * 获取一个域名下，所有junit类文件所有用例方法 list
	 * 
	 * @param domain
	 *            站点域名 eg. operate.trading.iapi.ymatou.com
	 * @param included
	 *            需要执行的tag 不需要时传入ALL
	 * @return 站点的所有 类跟方法映射 eg. Ts->Tc1,Ts->Tc2,...
	 */
	public static List<TestSuiteBeanV2> getTestMethods(String domain, String included) {

		List<TestSuiteBeanV2> junitCaseList = new java.util.LinkedList<TestSuiteBeanV2>();
		List<String> fileList = new ArrayList<String>();

		String path = "";
		String[] tempp = null;
		tempp = domain.split("\\.");
		for (String tp : tempp) {
			path = tp + File.separator + path;
		}
		// path 为 domain 反过来 加testcase目录
		path = System.getProperty("user.dir") + File.separator + "bin" + File.separator + path.substring(0, path.length()) + "testcase";
		// 该目录下所有文件路径
		fileList = TestDataManager.getFilelist(path);
		// 过滤old的情况
		for (String fname : fileList) {
			if (fname.contains("testcase"+ File.separator +"old")) {
				continue;
			}
			// 执行case的TestClass
			Class<?> cls = null;
			try {
				cls = TestDataManager.getClassNameByFileName(fname);
			} catch (Exception e) {
				Logger.debug(fname + ":不是一个java class,不处理！");
				e.printStackTrace();
			}
			if (cls != null) {
				// Logger.info("cls class is:"+cls);
				// 获取当前junit 类下所有用例方法
				Method[] ms = cls.getDeclaredMethods();
				for (Method m1 : ms) {
					if (m1.getName().startsWith("Tc_")) {
						Logger.info(fname + ":m1 method is:" + m1.getName());
						String categoryFlag = m1.getAnnotation(Category.class).toString().toUpperCase();

						TestSuiteBeanV2 TestSuiteBeanV2 = new TestSuiteBeanV2();

						// 默认跑所有级别case
						if (included.equals("ALL") || categoryFlag.contains(included)) {

							TestSuiteBeanV2.setTestSuiteClass(cls);
							TestSuiteBeanV2.setTestCaseName(m1.getName());
							junitCaseList.add(TestSuiteBeanV2);

						}
					}
				}
			}
		}
		return junitCaseList;
	}

	/**
	 * 初始化环境
	 * 
	 * @param env
	 */
	public static void initProp(String env) {
		EnvSetup.setEnv(env.toLowerCase());
		// 通过jenkins 跑case ，强制设置控制台不输出case 运行日志
		Logger.setDebug(false);

		Logger.info("jenkins 运行日志开启状态为：" + Logger.isDebug());
		String dns = EnvSetup.getSystemUrl("dns");
		if (TestDataManager.isWindowsOS()) {
			if (dns != null && !dns.equals("null") && dns.length() > 0) {
				TestDataManager.ChangeDns("本地连接", dns);
			}
		}
	}

	/**
	 * 返回所有执行失败用例的Map list class->case1,...
	 * 
	 * @param resultList
	 */
	private static List<TestSuiteBeanV2> getFailedCaseList(List<Result> resultList) {
		// 所有失败待重run的case
		List<TestSuiteBeanV2> failedCaseList = new java.util.LinkedList<TestSuiteBeanV2>();
		// failedtestCaseMap
		Set<String> failedSet = new HashSet<String>();
		resultList.forEach(result -> {
			result.getFailures().forEach(failure -> {
				String errtest = failure.getTestHeader();

				// junit 统计失败的case会算上error的情况。case如果有异常报错+断言失败，junit 会认为case失败2次
				// 去重
				if (errtest.startsWith("Tc_")) {
					failedSet.add(errtest);
				}
			});
		});

		// 将失败的case加到failedCaseList里
		failedSet.forEach(failedCase -> {
			Logger.debug("failedCase：" + failedCase);
			String cls = TestDataManager.getRegexList(failedCase, "(?<=\\().*?(?=\\))").get(0);
			String methodName = TestDataManager.getRegexList(failedCase, "^.*?(?=\\()").get(0);
			Class<?> JunitClass = null;
			try {
				JunitClass = (Class<?>) Class.forName(cls);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			TestSuiteBeanV2 TestSuiteBeanV2 = new TestSuiteBeanV2();
			TestSuiteBeanV2.setTestSuiteClass(JunitClass);
			TestSuiteBeanV2.setTestCaseName(methodName);

			failedCaseList.add(TestSuiteBeanV2);
		});
		return failedCaseList;
	}

	/**
	 * 删除结果文件夹及xml 结果文件
	 * 
	 * @param File
	 */
	private static void deleteResultFile(File file) {
		if (file.isDirectory()) {
			File[] temp = file.listFiles();
			for (File f : temp) {
				deleteResultFile(f);
			}
		}
		file.delete();
	}

	/**
	 * 根据域名列表，获取下面所有的case
	 * @param domain
	 * @param included
	 * @return
	 */
	public static List<TestSuiteBeanV2> getTestMethods(List<String> domains) {
		List<TestSuiteBeanV2> testMethods=new ArrayList<>();
		domains.forEach(b->{
			 List<TestSuiteBeanV2> c = getTestMethods(b, "ALL");
			 c.forEach(d->{
				 testMethods.add(d);
			 });
		});
		return testMethods;
	}
	/**
	 * 根据域名和文档的接口进行回归
	 * status  0：回归所有  1：回归最后一个case，2：回归最后两个case。。。
	 * @param urls
	 * @param status
	 * @throws Exception
	 */
	public static void RunCaseByUrl(List<String> urls,int status) throws Exception{

		//获取到域名下的所有方法
		List<TestSuiteBeanV2> testMethods = getTestMethods(urls);
		//获取文件中需要选择的case
		
		File file=new File("D:\\case.txt");
		InputStreamReader reader=new InputStreamReader(new FileInputStream(file),"GBK");
        BufferedReader bfreader=new BufferedReader(reader);
        String line;
        PrintWriter beanw=new PrintWriter(new FileWriter(file,true));
        while((line=bfreader.readLine())!=null) {//包含该行内容的字符串，不包含任何行终止符，如果已到达流末尾，则返回 null
        	line = line.replaceAll("\\s*", "").replaceAll("\\*", "");
        	String regex="(?:Ts_)[a-zA-Z]{1,50}";
            Pattern r = Pattern.compile(regex);
            Matcher m = r.matcher(line);
            if(m.find()){
            	String group = m.group(0);
            	//获取到文件中的类,然后根据所有方法，获取该类
            	List<TestSuiteBeanV2> testMethod=new ArrayList<>();
            	testMethods.forEach(b->{
            		String testCaseName = b.getTestCaseName();
            		String group1 = group.replaceAll("Ts", "Tc");
            		if(testCaseName.indexOf(group1)!=-1){
            			testMethod.add(b);
            		}
            	});
            	Map<Integer,TestSuiteBeanV2> maps=new HashMap<>();
            
            	if(status!=0){
            		testMethod.forEach(b->{
//                		//判断这些中。最大的一个
//                		System.out.println(b.getTestCaseName());
//                		String regex1="[1-9]\\d*$";
//                        Pattern r1 = Pattern.compile(regex1);
//                        Matcher m1 = r1.matcher(b.getTestCaseName());
//                        if(m1.find()){
//                        	int parseInt = Integer.parseInt(m1.group(0));
//                        	maps.put(parseInt, b);
//                        }
                	});
//            		Set<Integer> keySet = maps.keySet();
//            		Arrays.sort(obj);
//            		return obj[obj.size()-1];
            	}else {
            		List<Result> run = Run(testMethod,true);
            		run.forEach(b->{
//            			b.get
            		});
//            		testMethod.forEach(b->{
//            			System.out.println(b.getTestCaseName());
//            		});
				}
            	
            	
            	
            	
            }
        }
        
        beanw.close();
//        
//		testMethods.forEach(b->{
//			Class<?> testSuiteClass = b.getTestSuiteClass();//绝对路径
//			String testCaseName = b.getTestCaseName();//case名称
//			String[] split = testCaseName.split("_");
//			
//		});
	
	}
	public static void main1(String[] args) throws Exception {
		List<String> lists=new ArrayList<>();
		lists.add("livebuyer.iapi.ymatou.com");
		lists.add("liveseller.iapi.ymatou.com");
		lists.add("livevideo.ymatou.com");
		RunCaseByUrl(lists, 0);
	}
	public static void main(String args[]) {

		// System.out.println("args[0]:"+args[0]);
		// Map<String,Object> parameters=new HashMap();
		// String[] argsLst=args[0].split("&");
		// for(String str:argsLst)
		// {
		// String[] parameterList=str.split("=");
		// if(parameterList.length>=2)
		// parameters.put(parameterList[0], parameterList[1]);
		// else
		// parameters.put(parameterList[0], "");
		// }
		// if (type.toLowerCase().startsWith("--")){
		// 自选模式
		String param = "";
		for (int i = 0; i < args.length; i++) {
			param += args[i];
		}
		// 分割参数 --key=value --key2=value2
		String[] p = param.split("--");

		Map parameters = new HashMap<>();
		for (String s : p) {
			s = s.trim();
			String[] temp = s.split("=");
			// 正确分割会展示 不正确分割则不处理
			if (temp.length == 2) {
				parameters.put(temp[0].toLowerCase(), temp[1]);
				// System.out.println(temp[0].toLowerCase()+":"+parameters.get(temp[0].toLowerCase()));
			}
		}

		System.out.println("ENV:" + parameters.get("evn"));
		System.out.println("JOB_NAME:" + parameters.get("job_name"));
		System.out.println("BUILD_NUMBER:" + parameters.get("build_number"));
		System.out.println("PRIORITY:" + parameters.get("priority"));
		System.out.println("THREAD_NUM:" + parameters.get("thread_num"));
		System.out.println("RETRY_TIME:" + parameters.get("retry_time"));
		System.out.println("isshuffle:" + parameters.get("isshuffle"));
		System.out.println("TOEMAIL:" + parameters.get("toemail"));

		long before = System.currentTimeMillis();

		if (args.length < 2) {
			try {
				throw new Exception("至少需要2个参数:env runcase");
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		// 运行的环境
		// String env = args[0];
		String env = parameters.get("env").toString();
		// uat环境已经没有了 出现uat依然跑sit
		if (env.startsWith("UAT")) {
			env = "SIT1";
		}
		// 指定运行站点域名 api.trading.operate.ymatou.com
		// String runcase = args[1];
		String runcase = parameters.get("job_name").toString();
		try {
			// String thread = args[4];
			String thread = parameters.get("thread_num").toString();
			threadNum = Integer.valueOf(thread);
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "threadNum 配置项必须为数字");
			Logger.info("threadNum 默认为1个线程");
		}
		Logger.info("threadNum:" + threadNum);
		// 默认跑所有级别用例 ALL P1,;P2;P3
		String runIncluded = "ALL";
		try {
			// runIncluded = args[3].toUpperCase();
			runIncluded = parameters.get("priority").toString().toUpperCase();
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "  runIncluded 未配置");
			Logger.info("runIncluded 默认为ALL,跑所有case");
		}
		// 运行case 级别
		Logger.info("runIncluded:" + runIncluded);
		Logger.info("runcase:" + runcase);

		// 默认失败尝试重跑一次
		int retryTime = 1;
		try {
			// retryTime = Integer.valueOf(args[4]);
			retryTime = Integer.valueOf(parameters.get("retry_time").toString());
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "  retryTime 未配置");
			Logger.info("retryTime 默认为1,失败尝试重跑1次");
		}
		boolean isshuffle = false;
		try {
			// isshuffle = Boolean.valueOf(args[5]);
			isshuffle = Boolean.valueOf(parameters.get("isshuffle").toString());
		} catch (Exception e) {
			// TODO: handle exception
			Logger.info(e.getStackTrace() + "  isshuffle 未配置");
		}
		Logger.info("isshuffle:" + isshuffle);
		initProp(env);
		// 获取指定域名下所有用例

		List<TestSuiteBeanV2> junitTestCaseList = RunTestsV2.getTestMethods(runcase, runIncluded);

		Logger.info("需要执行的case 总数为" + junitTestCaseList.size());
		int allsize = junitTestCaseList.size();
		if (junitTestCaseList.size() < 1)
			System.exit(0);

		// 所有用例运行结果List
		List<Result> resultList = RunTestsV2.Run(junitTestCaseList, isshuffle);

		long after = System.currentTimeMillis();

		int runCount = (int) resultList.stream().mapToInt(result -> result.getRunCount()).summaryStatistics().getSum();

		/*
		 * int failureCount = (int) resultList.stream() .mapToInt(result ->
		 * result.getFailureCount()) .summaryStatistics().getSum();
		 */
		int ignoreCount = (int) resultList.stream().mapToInt(result -> result.getIgnoreCount()).summaryStatistics().getSum();

		// 获取执行失败的case 列表
		List<TestSuiteBeanV2> failedCaseList = getFailedCaseList(resultList);

		Logger.info("第一次运行case结束============== ");
		Logger.info("Actual RunCount:" + runCount);
		Logger.info("PassedCount:" + (runCount - failedCaseList.size()));
		Logger.info("FailureCount:" + failedCaseList.size());
		Logger.info("IgnoreCount:" + ignoreCount);
		Logger.info("RunTime:" + (after - before) / 1000 + "s");
		ResultParser resultParser = new ResultParser();
		// 没有失败的退出
		if (failedCaseList.size() < 1){
			Logger.info("开始解析结果文件入库============== ");
			TestCaseWapper tcw = new TestCaseWapper();
			// 设置结果文件路径
			resultParser.setPath(RESULT_PATH);
			// 执行的批次号
			int passid = 0;
			try {
				passid = resultParser.parserXmlToDb(tcw, env, runcase);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				Logger.info("首次解析结果文件入库失败!" + e.getMessage());
				e.printStackTrace();
			}
			Logger.info("解析结果文件入库结束============== ");
			System.exit(0);
		}

		// 首次执行失败的case map list 集合
		List<Result> resultFailedList = new ArrayList<Result>();

		// 错误case 重跑只启用一个线程
		SERVICE.shutdown();

		threadNum = 1;

		int count = 1;
		while (retryTime > 0) {
			Logger.info("开始第" + count + "次重跑失败的case============== ");
			//失败可能是多线程导致 这里转换成单线程
			threadNum=1;
			resultFailedList = RunTestsV2.Run(failedCaseList, isshuffle,false);
//			// 解析失败重跑用例的结果文件入库
//			try {
//				resultParser.parserTestContentsToDb(tcw, passid);
//			} catch (Exception e) {
//				// TODO Auto-generated catch block
//				Logger.info("解析失败重跑用例的结果文件入库失败!" + e.getMessage());
//				e.printStackTrace();
//			}
			// 递归运行失败的测试用例
			failedCaseList = getFailedCaseList(resultFailedList);
			count++;
			retryTime--;
		}
		Logger.info("运行case结束============== ");
		Logger.info("开始解析结果文件入库============== ");
		TestCaseWapper tcw = new TestCaseWapper();
		// 设置结果文件路径
		resultParser.setPath(RESULT_PATH);
		// 执行的批次号
		int passid = 0;
		try {
			passid = resultParser.parserXmlToDb(tcw, env, runcase);
		} catch (Exception e) {
			// TODO Auto-generated catch block
			Logger.info("首次解析结果文件入库失败!" + e.getMessage());
			e.printStackTrace();
		}
		Logger.info("解析结果文件入库结束============== ");
		if (!SERVICE.isShutdown()) {
			SERVICE.shutdown();
		}

		// //通过率过低的情况下返回失败
		Double faildrate = Double.valueOf(failedCaseList.size()) / Double.valueOf(allsize);
		System.out.println("failedCaseList.size():" + failedCaseList.size());
		System.out.println("allsize:" + allsize);
		// Double faildrate=0.3;
		// if(faildrate>0.2 && args[args.length-1].contains("@")){
		if (faildrate > 0.2 &&parameters.get("toemail")!=null&& parameters.get("toemail").toString().contains("@")) {
			try {
				String toEmail = parameters.get("toemail").toString();
				SendEmailBean sendemailBean = new SendEmailBean();
				SendEmailCall sendemailCall = new SendEmailCall();
				System.out.println("发送邮件:" + toEmail);
				sendemailBean.setAppId("AutoTest");
				sendemailBean.setContent("站点：" + parameters.get("job_name") + " 自动化case失败率高于20%,请及时修改！");
				// 测试账号 配置在mail_sender 表中
				sendemailBean.setFromMail("alarm@ymatou.com");
				sendemailBean.setFromName("自动化测试邮件AutoTest");
				sendemailBean.setSubject("自动化测试邮件AutoTest");
				sendemailBean.setToMail(toEmail);
				sendemailCall.setData(false, sendemailBean);
				sendemailCall.callService();
				System.out.println("失败率高于20%,触发异常退出");
				System.exit(-1);
			} catch (IOException e) {
				e.printStackTrace();
				System.exit(-1);
			} finally {
				System.exit(0);
			}
		}
		System.exit(0);
	}
}
